package com.xxx.swagger;

import com.xxx.common.enums.base.BaseEnum;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.stereotype.Component;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.Lists.transform;
import static springfox.documentation.swagger.schema.ApiModelProperties.findApiModePropertyAnnotation;

@Component
public class ReqCodedEnumPropertyPlugin implements ExpandedParameterBuilderPlugin {
    @Override
    public void apply(ParameterExpansionContext context) {
        Optional<ApiModelProperty> apiModelPropertyOptional
                = findApiModePropertyAnnotation(context.getField().getRawMember());
        if (apiModelPropertyOptional.isPresent()) {
            fromApiModelProperty(context, apiModelPropertyOptional.get());
        }
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }
    private void fromApiModelProperty(ParameterExpansionContext context, ApiModelProperty apiModelProperty) {
        String allowableProperty = emptyToNull(apiModelProperty.allowableValues());
        AllowableValues allowable = allowableValues(fromNullable(allowableProperty), context.getField().getRawMember());
        if(context.getField().getRawMember().getType().isEnum()){

            //获取CodedEnum的code值
            BaseEnum[] values = (BaseEnum[]) context.getField().getRawMember().getType().getEnumConstants();
            final List<String> displayValues = Arrays.stream(values).map(codedEnum -> ((java.lang.Enum)codedEnum).name()+":"+codedEnum.getDescription()).collect(Collectors.toList());
            StringBuffer sb = new StringBuffer();
            for (String displayValue : displayValues) {
                sb.append(displayValue+"\r\n");
            }
            context.getParameterBuilder()
                    .description(sb.toString())
                    .required(apiModelProperty.required())
                    .allowableValues(allowable)
                    .parameterAccess(apiModelProperty.access())
                    .hidden(apiModelProperty.hidden())
                    .build();
        }else {
            context.getParameterBuilder()
                    .description(apiModelProperty.value())
                    .required(apiModelProperty.required())
                    .allowableValues(allowable)
                    .parameterAccess(apiModelProperty.access())
                    .hidden(apiModelProperty.hidden())
                    .build();
        }
    }

    private AllowableValues allowableValues(final Optional<String> optionalAllowable, final Field field) {

        AllowableValues allowable = null;
        if (field.getType().isEnum()) {
            allowable = new AllowableListValues(getEnumValues(field.getType()), "LIST");
        } else if (optionalAllowable.isPresent()) {
            allowable = ApiModelProperties.allowableValueFromString(optionalAllowable.get());
        }

        return allowable;
    }

    private List<String> getEnumValues(final Class<?> subject) {

        if ( BaseEnum.class.isAssignableFrom(subject)) {
            //获取CodedEnum的code值
            BaseEnum[] values = (BaseEnum[]) subject.getEnumConstants();
            List<String> displayValues = Arrays.stream(values).map(codedEnum -> ((java.lang.Enum)codedEnum).name()).collect(Collectors.toList());
            return  displayValues;
        }else{
            return transform(Arrays.asList(subject.getEnumConstants()), new Function<Object, String>() {
                @Override
                public String apply(final Object input) {
                    return input.toString();
                }
            });
        }


    }

}